# The goal behind this program is to simply stay alive to the end of the game, not necessarily win.
# In order to encourage others to cooperate (hunt) with me, a high hunting rate of 95% is employed in the
# first ten rounds. After that, the strategy is to hunt only when the expected food value of hunting is
# greater than 0. Assuming I hunt, my expected value is -3*(probability partner slacks) + 2*(probability
# hunting bonus is achieved by tribe). The phunt and pbonus functions are used to estimate these
# probabilities, respectively, based on what happened in similar situations in past rounds. I've never 
# coded in Python before, so the code is probably horribly inefficient but hopefully it is functional 

import random
import numpy
import scipy
from scipy import stats

# Global variables (all limited to last 100 rounds because earlier rounds are less likely to be indicative of the present state of the game
percent_needed = list()		# Hunting percent needed for bonus in each round
percent_achieved = list()	# Hunting percent achieved in each round
round_size = list()			# Number of players in each round 
past_partner_reputations = list()	# Reputations of each partner 
my_reputations = list()		# My reputations for each interaction in above array 
past_outcomes = list()		# Outcomes of past pairings: 1 if partner hunted, 0 otherwise

def pbonus(m,player_reputations):
    # Use outcomes of past rounds to estimate probability of hunting bonus being achieved
    num_players = len(player_reputations)
    target_percent = float(m) / (num_players*(num_players-1))	# percent needed to get bonus
    
    # Linear regression with past targets on x axis and past achievements on y axis 
    slope, intercept, r_value, p_value, std_err = stats.linregress(percent_needed,percent_achieved)
    prediction = intercept + slope*target_percent
    prob_bonus = 1.0 - stats.norm(prediction,std_err).cdf(target_percent)	# Normal distribution assumed (probably not entirely accurate)
    return prob_bonus

def phunt(current_reputation,partner_reputation):
    # Use outcome of past rounds to estimate probability of cooperation given my reputation and opponent's reputation
    n = len(past_parner_reputations)
    count = 0
    num_hunts = 0
    # Find average hunting rate for past rounds where reputations for each player were within 0.05 of current values
    for i in range(n):
        if ((past_partner_reputations[i] - 0.05) < partner_reputation < (past_partner_reputations[i] + 0.05)):
            if ((my_reputations[i] - 0.5) < current_reputation < (my_reputations[i] + 0.5)):
                count += 1
                num_hunts += past_outcomes[i]
    prob_hunt = num_hunts/count
    return prob_hunt

def hunt_choices(round_number, current_food, current_reputation, m,  player_reputations):

    hunt_decisions = list()
    num_players = len(player_reputations)
    
    # For first ten rounds, hunt with probability 0.95 to build a good reputation
    if round_number <= 10:
        for reputation in player_reputations:
            choice = 'h' if random.random() < 0.95 else 's'
            hunt_decisions.append(choice)
            
    # Dominant strategy is to always slack in two-person game
    elif num_players == 2:
        hunt_decisions = ['s' for x in player_reputations]

    # If I could theoretically die from hunting with every person, then slack to save food
    elif current_food < (3*num_players):
        hunt_decisions = ['s' for x in player_reputations]
    
    else:
        prob_bonus = pbonus(m,player_reputations)	# estimated prob of bonus being achieved
        for i in range(num_players):
            choice = 'h' if (2*prob_bonus - 3*phunt(current_reputation,player_reputations[i])) > 0 else 's'
            hunt_decisions.append(choice)
    
    global percent_needed
    global percent_achieved
    global round_size
    global past_partner_reputations
    global my_reputations
    global past_outcomes
    # If past round 100, get rid of information from 100 rounds ago 
    if round_number > 100:
        deletion_index = round_size[0]
        del percent_needed[0]
        del percent_achieved[0]
        del round_size[0]
        del past_partner_reputations[0:deletion_index]
        del my_reputations[0:deletion_index]
        del past_outcomes[0:deletion_index]
        
    # Add new information to global arrays
    percent_needed.append(float(m) / (num_players*(num_players-1)))
    round_size.append(num_players)
    past_partner_reputations.extend(player_reputations)
    for reputation in player_reputations:
        my_reputations.append(current_reputation)
        

    return hunt_decisions

def hunt_outcomes(food_earnings):
    
    # Update array of hunt/slack outcomes
    global past_outcomes

    for amount in food_earnings:
        if amount >= 0:		# Partner hunted
            past_outcomes.append(1)
        else:
            past_outcomes.append(0)
            

def round_end(award, m, number_hunters):

    num_players = round_size[-1]
    hunt_percent = number_hunters/(num_players*(num_players-1))
    
    # Update array of hunt percent
    global percent_achieved
    percent_achieved.append(hunt_percent)
